home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 011 / alctable.arc / ALCTABLE.PAS < prev   
Pascal/Delphi Source File  |  1986-06-25  |  12KB  |  343 lines

  1. {**************************************************************************
  2. *   Maps system memory blocks for MS/PCDOS 2.0 and higher.                *
  3. *   Copyright (c) 1986 Kim Kokkonen, TurboPower Software.                 *
  4. *   Released to the public domain for personal, non-commercial use only.  *
  5. ***************************************************************************
  6. *   written 1/2/86                                                        *
  7. *   revised 1/10/86 for                                                   *
  8. *     running under DOS 2.X, where block owner names are unknown          *
  9. *   revised 1/22/86 for                                                   *
  10. *     a bug in parsing the owner name of the block                        *
  11. *     a quirk in the way that the DOS PRINT buffer installs itself        *
  12. *     minor cosmetic changes                                              *
  13. *   revised 2/6/86 for (version 1.3)                                      *
  14. *     smarter filtering for processes that deallocate their environment   *
  15. ***************************************************************************
  16. *   telephone: 408-378-3672, CompuServe: 72457,2131.                      *
  17. *   requires Turbo version 3 to compile.                                  *
  18. *   Compile with mAx dynamic memory = A000.                               *
  19. *   limited to environment sizes of 255 bytes (default is 128 bytes)      *
  20. ***************************************************************************}
  21.  
  22. {$P128}
  23.  
  24. PROGRAM MapMem;
  25.   {-look at the system memory map using DOS memory control blocks}
  26. CONST
  27.   MaxBlocks = 100;
  28.   Version = '1.3';
  29. TYPE
  30.   Block = RECORD              {store info about each memory block as it is found}
  31.             idbyte : Byte;
  32.             mcb : Integer;
  33.             psp : Integer;
  34.             len : Integer;
  35.             psplen : Integer;
  36.             env : Integer;
  37.             cnt : Integer;
  38.           END;
  39.   BlockType = 0..MaxBlocks;
  40.   BlockArray = ARRAY[BlockType] OF Block;
  41.  
  42. VAR
  43.   Blocks : BlockArray;
  44.   BlockNum : BlockType;
  45.  
  46.   PROCEDURE FindTheBlocks;
  47.     {-scan memory for the allocated memory blocks}
  48.   CONST
  49.     MidBlockID = $4D;         {byte DOS uses to identify part of MCB chain}
  50.     EndBlockID = $5A;         {byte DOS uses to identify last block of MCB chain}
  51.   VAR
  52.     mcbSeg : Integer;         {potential segment address of an MCB}
  53.     nextSeg : Integer;        {computed segment address for the next MCB}
  54.     gotFirst : Boolean;       {true after first MCB is found}
  55.     gotLast : Boolean;        {true after last MCB is found}
  56.     idbyte : Byte;            {byte that DOS uses to identify an MCB}
  57.  
  58.     PROCEDURE StoreTheBlock(VAR mcbSeg, nextSeg : Integer;
  59.                             VAR gotFirst, gotLast : Boolean);
  60.       {-store information regarding the memory block}
  61.     VAR
  62.       nextID : Byte;
  63.       pspAdd : Integer;       {segment address of the current PSP}
  64.       mcbLen : Integer;       {size of the current memory block in paragraphs}
  65.     BEGIN
  66.  
  67.       mcbLen := MemW[mcbSeg:3]; {size of the MCB in paragraphs}
  68.       nextSeg := Succ(mcbSeg+mcbLen); {where the next MCB should be}
  69.       pspAdd := MemW[mcbSeg:1]; {address of program segment prefix for MCB}
  70.       nextID := Mem[nextSeg:0];
  71.  
  72.       IF gotLast OR (nextID = EndBlockID) OR (nextID = MidBlockID) THEN BEGIN
  73.         BlockNum := Succ(BlockNum);
  74.         gotFirst := True;
  75.         WITH Blocks[BlockNum] DO BEGIN
  76.           idbyte := Mem[mcbSeg:0];
  77.           mcb := mcbSeg;
  78.           psp := pspAdd;
  79.           env := MemW[pspAdd:$2C];
  80.           len := mcbLen;
  81.           psplen := 0;
  82.           cnt := 1;
  83.         END;
  84.       END;
  85.  
  86.     END {storetheblock} ;
  87.  
  88.   BEGIN
  89.     {start above the Basic work area, could probably start even higher}
  90.     {there must be a magic address to start from, but it is not documented}
  91.     mcbSeg := $50;
  92.     gotFirst := False;
  93.     gotLast := False;
  94.     BlockNum := 0;
  95.  
  96.     {scan all memory until the last block is found}
  97.     REPEAT
  98.       idbyte := Mem[mcbSeg:0];
  99.       IF idbyte = MidBlockID THEN BEGIN
  100.         StoreTheBlock(mcbSeg, nextSeg, gotFirst, gotLast);
  101.         IF gotFirst THEN mcbSeg := nextSeg ELSE mcbSeg := Succ(mcbSeg);
  102.       END ELSE IF gotFirst AND (idbyte = EndBlockID) THEN BEGIN
  103.         gotLast := True;
  104.         StoreTheBlock(mcbSeg, nextSeg, gotFirst, gotLast);
  105.       END ELSE
  106.         {still looking for first block, try every paragraph boundary}
  107.         mcbSeg := Succ(mcbSeg);
  108.     UNTIL gotLast;
  109.  
  110.   END {findtheblocks} ;
  111.  
  112.  
  113.   PROCEDURE ShowTheBlocks;
  114.     {-analyze and display the blocks found}
  115.   CONST
  116.     MaxVector = $40;          {highest interrupt vector checked for trapping}
  117.   TYPE
  118.     Pathname = STRING[64];
  119.     HexString = STRING[4];
  120.     Address = RECORD
  121.                 offset, segment : Integer;
  122.               END;
  123.     VectorType = 0..MaxVector;
  124.   VAR
  125.     st : Pathname;
  126.     b : BlockType;
  127.     dosV : Byte;
  128.     Vectors : ARRAY[VectorType] OF Address ABSOLUTE 0 : 0;
  129.     vTable : ARRAY[VectorType] OF Real;
  130.     SumBlocks : BlockType;
  131.     Sum : BlockArray;
  132.  
  133.     FUNCTION Hex(i : Integer) : HexString;
  134.       {-return hex representation of integer}
  135.     CONST
  136.       hc : ARRAY[0..15] OF Char = '0123456789ABCDEF';
  137.     VAR
  138.       l, h : Byte;
  139.     BEGIN
  140.       l := Lo(i); h := Hi(i);
  141.       Hex := hc[h SHR 4]+hc[h AND $F]+hc[l SHR 4]+hc[l AND $F];
  142.     END {hex} ;
  143.  
  144.     FUNCTION DOSversion : Byte;
  145.       {-return the major version number of DOS}
  146.     VAR
  147.       reg : RECORD
  148.               CASE Byte OF
  149.                 1 : (ax, bx, cx, dx, bp, si, di, ds, es, flags : Integer);
  150.                 2 : (al, ah, bl, bh, cl, ch, dl, dh : Byte);
  151.             END;
  152.     BEGIN
  153.       reg.ah := $30;
  154.       MsDos(reg);
  155.       DOSversion := reg.al;
  156.     END {dosversion} ;
  157.  
  158.     FUNCTION Cardinal(i : Integer) : Real;
  159.       {-return an unsigned integer 0..65535}
  160.     BEGIN
  161.       Cardinal := 256.0*Hi(i)+Lo(i);
  162.     END {cardinal} ;
  163.  
  164.     FUNCTION Owner(startadd : Integer) : Pathname;
  165.       {-return the name of the owner program of an MCB}
  166.     VAR
  167.       e : STRING[255];
  168.       i : Integer;
  169.       t : Pathname;
  170.  
  171.       PROCEDURE StripNonAscii(VAR t : Pathname);
  172.         {-return an empty string if t contains any non-printable characters}
  173.       VAR
  174.         ipos : Byte;
  175.         goodname : Boolean;
  176.       BEGIN
  177.         goodname := True;
  178.         FOR ipos := 1 TO Length(t) DO
  179.           IF (t[ipos] < ' ') OR (t[ipos] > '}') THEN
  180.             goodname := False;
  181.         IF NOT(goodname) THEN t := '';
  182.       END {stripnonascii} ;
  183.  
  184.       PROCEDURE StripPathname(VAR pname : Pathname);
  185.         {-remove leading drive or path name from the input}
  186.       VAR
  187.         spos, cpos, rpos : Byte;
  188.       BEGIN
  189.         spos := Pos('\', pname);
  190.         cpos := Pos(':', pname);
  191.         IF spos+cpos = 0 THEN Exit;
  192.         IF spos <> 0 THEN BEGIN
  193.           {find the last slash in the pathname}
  194.           rpos := Length(pname);
  195.           WHILE (rpos > 0) AND (pname[rpos] <> '\') DO rpos := Pred(rpos);
  196.         END ELSE
  197.           rpos := cpos;
  198.         Delete(pname, 1, rpos);
  199.       END {strippathname} ;
  200.  
  201.     BEGIN
  202.       {get the environment string to scan}
  203.       e[0] := #255;
  204.       Move(Mem[startadd:0], e[1], 255);
  205.  
  206.       {find end of the standard environment}
  207.       i := Pos(#0#0, e);
  208.       IF i = 0 THEN BEGIN
  209.         {something's wrong, exit gracefully}
  210.         Owner := '';
  211.         Exit;
  212.       END;
  213.  
  214.       {end of environment found, get the program name that follows it}
  215.       t := '';
  216.       i := i+3;               {skip over #0#0#args}
  217.       REPEAT
  218.         t := t+Chr(Mem[startadd:i]);
  219.         i := Succ(i);
  220.       UNTIL (Length(t) > 64) OR (Mem[startadd:i] = 0);
  221.  
  222.       StripNonAscii(t);
  223.       IF Length(t) = 0 THEN
  224.         Owner := 'N/A'
  225.       ELSE BEGIN
  226.         StripPathname(t);
  227.         IF t = '' THEN t := 'N/A';
  228.         Owner := t;
  229.       END;
  230.  
  231.     END {owner} ;
  232.  
  233.     PROCEDURE InitVectorTable;
  234.       {-build real equivalent of vector addresses}
  235.     VAR
  236.       v : VectorType;
  237.  
  238.       FUNCTION RealAdd(a : Address) : Real;
  239.         {-return the real equivalent of an address (pointer)}
  240.       BEGIN
  241.         WITH a DO
  242.           RealAdd := 16.0*Cardinal(segment)+Cardinal(offset);
  243.       END {realadd} ;
  244.  
  245.     BEGIN
  246.       FOR v := 0 TO MaxVector DO
  247.         vTable[v] := RealAdd(Vectors[v]);
  248.     END {initvectortable} ;
  249.  
  250.     PROCEDURE WriteHooks(start, stop : Integer);
  251.       {-show the trapped interrupt vectors}
  252.     VAR
  253.       v : VectorType;
  254.       sadd, eadd : Real;
  255.     BEGIN
  256.       sadd := 16.0*Cardinal(start);
  257.       eadd := 16.0*Cardinal(stop);
  258.       FOR v := 0 TO MaxVector DO BEGIN
  259.         IF (vTable[v] >= sadd) AND (vTable[v] <= eadd) THEN
  260.           Write(Copy(Hex(v), 3, 2), ' ');
  261.       END;
  262.     END {writehooks} ;
  263.  
  264.     PROCEDURE SortByPSP(VAR Blocks : BlockArray; BlockNum : BlockType);
  265.       {-sort in order of ascending PSP}
  266.     VAR
  267.       i, j : BlockType;
  268.       temp : Block;
  269.     BEGIN
  270.       FOR i := 1 TO Pred(BlockNum) DO
  271.         FOR j := BlockNum DOWNTO Succ(i) DO
  272.           IF Cardinal(Blocks[j].psp) < Cardinal(Blocks[Pred(j)].psp) THEN BEGIN
  273.             temp := Blocks[j];
  274.             Blocks[j] := Blocks[Pred(j)];
  275.             Blocks[Pred(j)] := temp;
  276.           END;
  277.     END {SortByPSP} ;
  278.  
  279.     PROCEDURE SumTheBlocks(VAR Blocks : BlockArray;
  280.                            BlockNum : BlockType;
  281.                            VAR Sum : BlockArray;
  282.                            VAR SumBlocks : BlockType);
  283.       {-combine the blocks with equivalent PSPs}
  284.     VAR
  285.       prevpsp : Integer;
  286.       b : BlockType;
  287.     BEGIN
  288.       SumBlocks := 0;
  289.       prevpsp := $FFFF;
  290.       FOR b := 1 TO BlockNum DO BEGIN
  291.         IF Blocks[b].psp <> prevpsp THEN BEGIN
  292.           SumBlocks := Succ(SumBlocks);
  293.           Sum[SumBlocks] := Blocks[b];
  294.           prevpsp := Blocks[b].psp;
  295.         END ELSE
  296.           WITH Sum[SumBlocks] DO BEGIN
  297.             cnt := Succ(cnt);
  298.             len := len+Blocks[b].len;
  299.           END;
  300.         {get length of the block which owns the executable program}
  301.         {for checking vector trapping next}
  302.         IF Succ(Blocks[b].mcb) = Blocks[b].psp THEN
  303.           Sum[SumBlocks].psplen := Blocks[b].len;
  304.       END;
  305.     END {sumblocks} ;
  306.  
  307.   BEGIN
  308.     WriteLn;
  309.     WriteLn('      Allocated Memory Map - by TurboPower Software - Version ', Version);
  310.     WriteLn;
  311.     WriteLn('PSP adr MCB adr  paras   bytes   owner        hooked vectors');
  312.     WriteLn('------- ------- ------- ------- ----------   ------------------------------');
  313.  
  314.     dosV := DOSversion;
  315.     InitVectorTable;
  316.     SortByPSP(Blocks, BlockNum);
  317.     SumTheBlocks(Blocks, BlockNum, Sum, SumBlocks);
  318.  
  319.     FOR b := 1 TO SumBlocks DO WITH Sum[b] DO BEGIN
  320.       Write(' ',
  321.       Hex(psp), '    ',       {PSP address}
  322.       Hex(mcb), '    ',       {MCB address}
  323.       Hex(len), '   ',        {size of block in paragraphs}
  324.       16.0*Cardinal(len):6:0, '  '); {size of block in bytes}
  325.  
  326.       {get the program owning this block by scanning the environment}
  327.       IF (dosV >= 3) AND (cnt > 1) THEN
  328.         st := Owner(env)
  329.       ELSE
  330.         st := 'N/A';
  331.       WHILE Length(st) < 13 DO st := st+' ';
  332.       Write(st);
  333.       WriteHooks(psp, psp+psplen);
  334.       WriteLn;
  335.     END;
  336.  
  337.   END {showtheblocks} ;
  338.  
  339. BEGIN
  340.   FindTheBlocks;
  341.   ShowTheBlocks;
  342. END.
  343.